package demo.dcs.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.management.ManagementFactory;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.NumberFormat;
import java.util.Date;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 唯一编号，经测试作者本人的笔记本平均每毫秒能生成30条左右唯一ID，单机平均每秒30000个唯一ID
 * 本类会默认生成一个长度为19位的唯一ID，可支持同一局域网内多机超高并发不唯一，最长支持到2286年(请在此日期前更换id生成机制)
 * 多局域网共享同一数据库的话，高并发情况下可能会产生碰撞（解决办法是依靠19位之外的数据位，假如你的订单是32位的你可以取其中两位作为机房编号，通过配置项的形式进行配置）
 * 你可以在此订单号的基础上加入一些其他的标志位，用以拓展订单信息（日期等）或适应更复杂的编号生成环境，如多局域网（多机房）。
 * IP后缀（192.168.20.12 -> 012）+ 时间戳 + 序列号
 */
public class XUID {
    private static Logger logger = LoggerFactory.getLogger(XUID.class);
    private static AtomicLong atomicLong = new AtomicLong(0L);
    private static AtomicLong timestamp = new AtomicLong(System.currentTimeMillis());
    private static final Long DEFAULT_MAX = 999L;
    private static String ip_postfix;
    private static NumberFormat numberFormat;

    static {
        try {
            InetAddress ia = InetAddress.getLocalHost();
            String[] split = ia.getHostAddress().split("\\.");
            ip_postfix = split[split.length - 1];
            NumberFormat numberFormat = NumberFormat.getNumberInstance();
            numberFormat.setGroupingUsed(false);
            numberFormat.setMinimumIntegerDigits(3);
            String name = ManagementFactory.getRuntimeMXBean().getName();  
            String pid = name.split("@")[0];
            ip_postfix = numberFormat.format(Long.parseLong(ip_postfix) + Long.parseLong(pid)%512);
        } catch (UnknownHostException e) {
            logger.error("XUID error: ", e);
        }

        numberFormat = NumberFormat.getNumberInstance();
        int seuquenceLen = String.valueOf(DEFAULT_MAX).length();
        numberFormat.setMinimumIntegerDigits(seuquenceLen);
        numberFormat.setGroupingUsed(false);
    }

    public static void main(String[] args) {
        System.out.println(Long.valueOf(getUniqueId()));
        Long maxtimstamp=9999999999999L;
        Date date=new Date(maxtimstamp);
        System.out.println(date.toString());
    }

    public static String getUniqueId() {
        String formatSequence = numberFormat.format(getSequence(DEFAULT_MAX));

        return ip_postfix +System.currentTimeMillis()+ formatSequence;
    }

    private static String getUniqueId(Long maxPerMillisecond) {
        if (maxPerMillisecond == null)
            maxPerMillisecond = DEFAULT_MAX;
        NumberFormat numberFormat = NumberFormat.getNumberInstance();
        numberFormat.setMinimumIntegerDigits(String.valueOf(DEFAULT_MAX).length());
        Long sequence = getSequence(maxPerMillisecond);
        String formatSequence = numberFormat.format(sequence);

        return ip_postfix +System.currentTimeMillis()+ formatSequence;
    }

    /**
     * get a sequence（number） in a millisecond,if sequenceNumber equels maxPerMillisecond,
     * it will return at next millisecond with a sequenceNumber which will increase from zero again
     *
     * @param maxPerMillisecond
     * @return
     */
    public static Long getSequence(Long maxPerMillisecond) {
        long nextSequence = atomicLong.get();
        while (nextSequence >= maxPerMillisecond || !atomicLong.compareAndSet(nextSequence, ++nextSequence)) {
            if (nextSequence >= maxPerMillisecond && timestamp.longValue() >= System.currentTimeMillis()) {
                nextSequence = atomicLong.get();
            } else if (nextSequence >= maxPerMillisecond) {
                if (atomicLong.compareAndSet(maxPerMillisecond, 0)) {
                    timestamp.set(System.currentTimeMillis());
                    nextSequence = 0L;
                }
            }
        }

        return nextSequence;
    }


}